package se.karolinska.corticostriatal.crosshair;

import ij.ImagePlus;
import ij.gui.ImageWindow;
import ij.gui.Overlay;
import ij.gui.Roi;
import ij.gui.ShapeRoi;
import java.awt.Color;
import java.awt.geom.GeneralPath;
import org.micromanager.MMStudioMainFrame;
import org.micromanager.utils.ReportingUtils;

/**
 *  Crosshair Overlay: projects a crosshair onto the live image window. 
 * 
 *  Since this creates an overlay, stored images will not be affected, and 
 *  no persistent change is made to the actual image; rather, this adds another
 *  layer on top of the life view window.
 * 
 *  @author Matthijs
 */
public class CrosshairOverlay {
    
    private static  CrosshairOverlay instance;                  // Singleton instance.
    private         Overlay          overlay; 
    private         double           size       = 0.5;          // Default size.
    private         Color            color      = Color.RED;    // Default color.
    private         CrosshairOverlay () { }                     // Private constructor.
    
    
    /**
     *  Singleton instance retrieval.
     * 
     *  @return An instance of CrosshairOverlay. 
     */
    public static CrosshairOverlay getInstance () {
        if (instance == null)
            instance =  new CrosshairOverlay();
        return instance;
    }    
    
    
    /**
     *  Change the size of the crosshair. This should be a percentage of the 
     *  image size. The default slider range encompasses [0, 100].
     * 
     *  Note: rather than actually changing the overlay, this destroys and
     *  recreates the overlay with the desired new size.
     * 
     *  @param size The sizeSlider value.
     */
    public void setSize (int size) {
        this.size = size * 0.01;
        refresh();
    }
    
    
    /**
     *  Change the color of the crosshair. Takes a Color object as argument, 
     *  which can be directly used to set the stroke color.
     * 
     *  Refreshes the overlay after setting so any changes come into effect
     *  immediately.
     * 
     *  @param color 
     */
    public void setColor (Color color) {
        this.color = color;
        refresh();
    }
    
        
    /**
     *  Create a new overlay with the desired characteristics as set through
     *  setSize and setColor. This can fail if no live image window is found,
     *  or if something unspeakable happens during creation of the overlay.
     * 
     *  @throws Exception 
     */
    public void show () throws Exception {
        ImagePlus image = getLiveWindowImage();
        if (image == null)
            throw new Exception("Live image window required.");
            
        int width   = image.getWidth();
        int height  = image.getHeight();
        
        int dWidth  = (int) (size * width)  / 2;
        int dHeight = (int) (size * height) / 2;
        
        GeneralPath path = new GeneralPath();
        path.moveTo(width / 2 - dWidth, height / 2 - dHeight);
        path.lineTo(width / 2 + dWidth, height / 2 + dHeight);
        path.moveTo(width / 2 + dWidth, height / 2 - dHeight);
        path.lineTo(width / 2 - dWidth, height / 2 + dHeight);
        Roi roi     = new ShapeRoi(path);
        roi.setStrokeColor(color);
        overlay     = new Overlay(roi);
        image.setOverlay(overlay);
    }
    
    
    /**
     *  Clear the overlay. This removes any and all trace of the overlay
     *  from the live image window. A call to show() is required to 
     *  recreate the overlay.
     */
    public void hide () {
        if (overlay != null)
            overlay.clear();
    }
    
    
    /**
     *  Attempt to clear and recreate the overlay. This could, but should not,
     *  fail if the overlay was working before this call.
     */
    private void refresh () {
        try {
            hide();
            show();
        } catch (Exception e) {
            ReportingUtils.logError("Could not show crosshair after changing size.");
        }        
    }
    
    
    /**
     *  Get reference to the live image window, through the MicroManager
     *  studioMainFrame instance. 
     * 
     *  @return Reference to the ImagePlus object associated with the live
     *          window. May return null if no live image is currently active.
     */
    private ImagePlus getLiveWindowImage () {
        MMStudioMainFrame   manager    = MMStudioMainFrame.getInstance();
        ImageWindow         window     = manager.getImageWin();
        if (window == null) 
            return null;
        
        return window.getImagePlus();
    }
}